commonlibsse_ng\rel\relocation/
phantom_member.rs

1use crate::skse::version::RUNTIME_SSE_1_6_629;
2
3use super::{RelocationError, relocate_member_if_newer, relocate_member_if_newer_mut};
4use core::{fmt, marker::PhantomData};
5
6/// A zero-sized marker used to access dynamically relocated members.
7///
8/// In the C++ class, it is not possible to simultaneously obtain mutable references
9/// to multiple fields due to Rust's borrowing rules. To work around this, `PhantomMember` provides
10/// an API for safely accessing individual fields while respecting the newer runtime offsets.
11///
12/// This struct is a placeholder that allows access to relocated members by providing
13/// runtime-based offset calculations.
14///
15/// # Generics
16/// - `T`: Member type
17/// - `OLD`: Offset when < ver.1_6_629
18/// - `NEW`: Offset when >= ver.1_6_629
19#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct PhantomMember<T, const OLD: isize, const NEW: isize> {
21    marker: PhantomData<T>,
22}
23
24impl<T, const OLD: isize, const NEW: isize> fmt::Debug for PhantomMember<T, OLD, NEW>
25where
26    T: fmt::Debug,
27{
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        f.debug_struct("PhantomMember")
30            .field("type", &self.get())
31            .field("OLD", &OLD)
32            .field("NEW", &NEW)
33            .finish()
34    }
35}
36
37impl<T, const OLD: isize, const NEW: isize> Clone for PhantomMember<T, OLD, NEW> {
38    #[inline]
39    fn clone(&self) -> Self {
40        Self { marker: self.marker }
41    }
42}
43
44impl<T, const OLD: isize, const NEW: isize> PhantomMember<T, OLD, NEW> {
45    /// Retrieves a reference to the member as immutable.
46    ///
47    /// # Safety
48    /// This performs an unsafe relocation based on runtime version checks.
49    /// Returns `None` if the relocation fails.
50    ///
51    /// # Errors
52    /// - This function may return an error if the module's state cannot be accessed, or if the `map_active` call fails when fetching the current version.
53    /// - If the pointer is null
54    /// - If the pointer is unaligned
55    #[inline]
56    pub fn get(&self) -> Result<&T, RelocationError> {
57        unsafe { relocate_member_if_newer(RUNTIME_SSE_1_6_629, self, OLD, NEW) }
58    }
59
60    /// Retrieves a mutable reference to the member.
61    ///
62    /// # Safety
63    /// This performs an unsafe relocation based on runtime version checks.
64    /// Returns `None` if the relocation fails.
65    ///
66    /// # Errors
67    /// - This function may return an error if the module's state cannot be accessed, or if the `map_active` call fails when fetching the current version.
68    /// - If the pointer is null
69    /// - If the pointer is unaligned
70    #[inline]
71    pub fn get_mut(&mut self) -> Result<&mut T, RelocationError> {
72        unsafe { relocate_member_if_newer_mut(RUNTIME_SSE_1_6_629, self, OLD, NEW) }
73    }
74}